﻿using System;
using System.Collections.ObjectModel;
using System.Xml.Serialization;
using EpiNEXT.Foundation;
using EpiNEXT.Services.Collections;
using EpiNEXT.Services.Fields;
using EpiNEXT.Services.Validation;

namespace EpiNEXT.Services
{
    //[Serializable()]
    public abstract class DataEntryInstrument
    {
        #region Members
        private string _title = "Form";
        private int _maxRecords = Int32.MaxValue;
        private FieldCollection _fields = new FieldCollection();
        private RecordCollection _records = new RecordCollection();
        private static object _recordsLock = new object();
        #endregion // Members

        #region Constructors
        protected DataEntryInstrument()
        {
            this._records = new RecordCollection();
            //Record defaultRecord = new Record();
            //defaultRecord.Initialize(this);
            //this._records.Add(defaultRecord);
        }
        #endregion // Constructors

        #region Properties
        /// <summary>
        /// Data entry validation conditions
        /// </summary>
        [XmlArray("ValidationBehaviors")]
        [XmlArrayItem("Behavior", typeof(Behavior))]
        public ObservableCollection<Behavior> ValidationBehaviors { get; set; }

        /// <summary>
        /// The date/time the project was created
        /// </summary>
        [XmlIgnore]
        public DateTime DateCreated { get; set; }

        /// <summary>
        /// The date/time the project was last saved
        /// </summary>
        [XmlIgnore]
        public DateTime DateLastSaved { get; set; }

        [XmlElement]
        public long LastSaved
        {
            get { return DateLastSaved.Ticks; }
            set { DateLastSaved = new DateTime(value); }
        }

        [XmlElement]
        public long Created
        {
            get { return DateCreated.Ticks; }
            set { DateCreated = new DateTime(value); }
        }

        /// <summary>
        /// The ID that represents this form
        /// </summary>
        [XmlElement("Id")]
        public Guid FormId { get; set; }

        /// <summary>
        /// The title for the form
        /// </summary>
        [XmlElement("Title")]
        public string Title
        {
            get
            {
                return this._title;
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                {
                    throw new InvalidOperationException("Form title cannot be blank.");
                }
                else if (value.Length > 64)
                {
                    throw new InvalidOperationException("Form title cannot exceed 64 characters.");
                }
                else if (!System.Text.RegularExpressions.Regex.IsMatch(value, @"\w"))
                {
                    throw new InvalidOperationException("Form title must include only letters, numbers, and underscores.");
                }
                else
                {
                    double firstChar = -1;
                    bool success = double.TryParse(value[0].ToString(), out firstChar);
                    if (success)
                    {
                        throw new InvalidOperationException("Form title may not start with a digit.");
                    }

                    this._title = value;
                }
            }
        }

        [XmlIgnore]
        public Project Owner { get; set; }

        [XmlElement("IsRelatedForm")]
        public bool IsRelatedForm { get; set; }

        [XmlElement]
        public int MaxRecords
        {
            get
            {
                return _maxRecords;
            }
            set
            {
                if (value <= 0) throw new InvalidOperationException("Max records cannot be zero or less than zero.");
                _maxRecords = value;
            }
        }

        [XmlIgnore]
        public bool HasUnsavedChanges { get; set; }

        [XmlIgnore]
        public Form ParentForm { get; set; }

        [XmlElement("ParentFormId")]
        public Guid ParentFormId { get; set; }

        /// <summary>
        /// A list of fields on the form
        /// </summary>
        [XmlIgnore]
        public FieldCollection Fields
        {
            get
            {
                return this._fields;
            }
            set
            {
                this._fields = value;
            }
        }

        [XmlElement("Fields")]
        public FieldsDTO FieldsDTO { get; set; }

        /// <summary>
        /// A list of fields on the form
        /// </summary>
        [XmlIgnore]
        public RecordCollection Records
        {
            get
            {
                return this._records;
            }
        }
        #endregion // Properties

        #region Methods
        /// <summary>
        /// Gets the level at which this form appears in a relational hierarchy.
        /// </summary>
        /// <returns>int; represents the level at which the form appears in the hierarchy. 0 = top-most parent, 1 = child of top-most parent, 2 = grandchild of top-most parent, etc.</returns>
        public int GetRelationalLevel()
        {
            if (this.ParentForm == null)
            {
                return 0;
            }
            else
            {
                int level = 0;
                DataEntryInstrument parent = this.ParentForm;
                while (parent != null)
                {
                    parent = parent.ParentForm;
                    level++;
                }
                return level;
            }
        }

        /// <summary>
        /// Sets a special binding operation to the record collection so that the collection can be accessed
        /// on a thread other than the UI thread.
        /// </summary>
        //public virtual void InitalizeCollectionSynchronization()
        //{
        //    System.Windows.Data.BindingOperations.EnableCollectionSynchronization(Records, _recordsLock);
        //}

        /// <summary>
        /// Deletes a record from the form's current set of records
        /// </summary>
        /// <param name="record">The record to remove</param>
        /// <returns>bool; whether the operation was successful</returns>
        public bool DeleteRecord(Record record)
        {
            return this.Records.Remove(record);
        }

        /// <summary>
        /// Adds a record to the form's current set of records
        /// </summary>
        /// <param name="userName">The name of the user that added this record</param>
        /// <returns>record; the record that was added</returns>
        public Record AddRecord(string userName)
        {
            Record record = new Record(userName);
            record.Initialize(this);
            record.SuppressErrorNotification = false;
            this.Records.Add(record);
            return record;
        }

        /// <summary>
        /// Adds a record to the form's current set of records with a foreign key
        /// </summary>
        /// <param name="userName">The name of the user that added this record</param>
        /// <param name="foreignKey">The foreign key to attach</param>
        /// <returns>record; the record that was added</returns>
        public Record AddRecord(string userName, Guid foreignKey)
        {
            Record record = new Record(userName);
            record.Initialize(this, foreignKey);
            record.SuppressErrorNotification = false;
            this.Records.Add(record);
            return record;
        }
        #endregion // Methods
    }
}
